//problem2.cc
#include "copyright.h"
//problem2.cc

#include "copyright.h"
#include "system.h"
#include "thread.h"
#include "theater.h"

unsigned int totalCustomer;
Lock * totalCustomerLock;

unsigned int totalCustomerEntered;
Lock * totalCustomerEnteredLock;

unsigned int totalCustomerLeft;
Lock * totalCustomerLeftLock;


//define for the global variables
unsigned int numGroups;
unsigned int numCustomers;
unsigned int numTicketClerks;
unsigned int numConcessionClerks;
unsigned int numTicketTakers;
unsigned int numGroupsRemaining;

int seats[SEATS_SIZE];

//customer lines for the ticketclerk
unsigned int ticketclrk_cus_count[TICKETCLERKSIZE];



//ticketclerk's state to show whether the ticket clerk is busy or nor
unsigned int ticketclrk_state[TICKETCLERKSIZE];
unsigned int ticketclrk_off[TICKETCLERKSIZE];//used for the manager to break the ticketclerk off or not
// 1 means the clerk could off work and break, 0 means keep on work


//lock for the communications
Lock *ticketClerkLineLock; //used for the clerk and the customer to operate the line
Lock *ticketClerkMsgLock[TICKETCLERKSIZE]; //used for the clerk and the customer to operate the shared
//variables as tickets_num and so on.
//condition values for the ticketclerk and the customer to communicate
Condition  *ticketclerkline_cv[TICKETCLERKSIZE]; //used for the clerk to know
Condition  *ticketclerkMsg_cv[TICKETCLERKSIZE]; //used for the clerk and customer to transfer msg





//these varibles defined to commnunicate between the customers and ticketclerk.
//for example, the ticketclerks ask the customer how many tickets wants to buy, and the
//customer could set the global varibles like 4 tickets so the clerk could know how many,
//and continue the communication

//varibles used for customer and ticketclerk
unsigned int tickets_num[TICKETCLERKSIZE];     //ticket number the customer want to buy
unsigned int tickets_money[TICKETCLERKSIZE];  //ticket money the customer should pay


//for the concession clerk and the customer
//lock for the communication
Lock *concessClerkLineLock; //used for the clerk and the customer to operate the line
Lock *concessClerkMsgLock[CONCESSIONCLERK_SIZE]; //used for the clerk and the customer to operate the shared
//variables as tickets_num and so on.
//condition values for the ticketclerk and the customer to communicate
Condition  *concessclerkline_cv[CONCESSIONCLERK_SIZE]; //used for the clerk to know
Condition  *concessclerkMsg_cv[CONCESSIONCLERK_SIZE]; //used for the clerk and customer to transfer msg

//customer lines for the ticketclerk
unsigned int concessclrk_cus_count[CONCESSIONCLERK_SIZE];

//ticketclerk's state to show whether the ticket clerk is busy or nor
unsigned int concessclrk_state[CONCESSIONCLERK_SIZE];
unsigned int concessclrk_off[CONCESSIONCLERK_SIZE];//used for the manager to break the concerssion off or not
// 1 means the clerk could off work and break, 0 means keep on work

//varibles usef for customer and concession clerk
unsigned int popcorn_num[CONCESSIONCLERK_SIZE];
unsigned int soda_num[CONCESSIONCLERK_SIZE];
unsigned int food_money[CONCESSIONCLERK_SIZE];

//varibles used to record
unsigned int ticketclrk_totalticksnum[TICKETCLERKSIZE];

unsigned int popcorn_totalnum[CONCESSIONCLERK_SIZE];
unsigned int soda_totalnum[CONCESSIONCLERK_SIZE];
unsigned int food_totalmoney[CONCESSIONCLERK_SIZE];

unsigned int all_foodmoney = 0;
unsigned int all_ticketmoney = 0;

//defined for the headcustomer to communicate with the regular customers
Lock * customerLock[MAX_CUSTOMERS];
unsigned int customer_popcorn[MAX_CUSTOMERS];
unsigned int customer_soda[MAX_CUSTOMERS];
unsigned int customer_seatno[MAX_CUSTOMERS];
unsigned int customer_needbath[MAX_CUSTOMERS];
Condition * customer_cv[MAX_CUSTOMERS];
Semaphore * customer_sem[MAX_CUSTOMERS];
Lock * customer_foodLock[MAX_CUSTOMERS];
Lock * customer_seatLock[MAX_CUSTOMERS];



//defined for the ticket taker
//for the tiket taker and the customer
//lock for the communication
Lock  *seatsRsvLock; //used to count how many customers have entered the room
Lock *ticketTakerLineLock; //used for the clerk and the customer to operate the line
Lock *ticketTakerMsgLock[TICKETTAKER_SIZE]; //used for the clerk and the customer to operate the shared
//variables as concesss_num and so on.
//condition values for the ticketTaker and the customer to communicate
Condition  *ticketTakerline_cv[TICKETTAKER_SIZE]; //used for the clerk to know
Condition  *ticketTakerMsg_cv[TICKETTAKER_SIZE]; //used for the clerk and customer to transfer msg

//customer lines for the ticketTaker
unsigned int ticketTaker_cus_count[TICKETTAKER_SIZE];


//ticketTaker's state t o show whether the concess clerk is busy or nor
unsigned int ticketTaker_state[TICKETTAKER_SIZE];
unsigned int ticketTaker_off[TICKETTAKER_SIZE];//used for the manager to break the concerssion off or not
// 1 means the clerk could off work and break, 0 means keep on work

unsigned int ticTkr_ready=READYTOTAKETIC;//changed by the manager to know whether it is time to take the ticket

unsigned int seats_from[TICKETTAKER_SIZE];  //ticket money the customer should pay
unsigned int seats_to[TICKETTAKER_SIZE];  //ticket money the customer should pay
unsigned int seats_result[TICKETTAKER_SIZE];

//global varables for ticket taker and the customers
unsigned int tictaker_tic_number[TICKETTAKER_SIZE];
unsigned int seatsReserved;
unsigned int room_number[TICKETTAKER_SIZE];

//used for the movie technician
unsigned int movie_state;
//Lock  *moviePlayLock;
//Condition  *moviePlay_cv;


Lock  *movie_stateLock;
Condition  *movieState_cv;


//used to check the room is full or not, if the room is full, the movie technician could start the movie
unsigned int RoomFull;
Lock  *roomfullLock;
//Condition  *roomfull_cv;


Lock *ticTkrReadyLock[TICKETTAKER_SIZE];
Condition *ticTkrReady_cv[TICKETTAKER_SIZE];

//Lock  *movieReadyLock;
//Condition  *movieReady_cv;

Lock * movieTechMsgLock;
Condition * movieTechMsg_cv;

unsigned int ticketclrk_addNewline[TICKETCLERKSIZE];
unsigned int foodclrk_addNewline[CONCESSIONCLERK_SIZE];
unsigned int tickettaker_addNewline[TICKETTAKER_SIZE];

//this function do the init job for the whole program
void initTheater()
{
    int i=0;
    char * name = new char [20];
    //lock for the communication
    for(i=0; i<MAX_CUSTOMERS; i++)
    {
        sprintf(name,"customerLock%d",i);
        customerLock[i]=new Lock(name);
        sprintf(name,"customer_cv%d",i);
        customer_cv[i]=new Condition(name);
        sprintf(name,"customer_sem%d",i);
        customer_sem[i]=new Semaphore(name,0);
        sprintf(name,"customer_foodLock%d",i);
        customer_foodLock[i]=new Lock(name);
        sprintf(name,"customer_seatLock%d",i);
        customer_seatLock[i]=new Lock(name);
    }
    //step 1: init lock and conditions for the ticket clerk


    //lock for the communication
    sprintf(name,"ticketClerkLineLock");
    ticketClerkLineLock=new Lock(name);
    for(i=0; i<TICKETCLERKSIZE; i++)
    {
        sprintf(name,"ticketClerkMsgLock%d",i);
        ticketClerkMsgLock[i]=new Lock(name);
        sprintf(name,"ticketclerkline_cv%d",i);
        ticketclerkline_cv[i]=new Condition(name);
        sprintf(name,"ticketclerkMsg_cv%d",i);
        ticketclerkMsg_cv[i]=new Condition(name);
    }
    //step 2:init lock and conditions for the concession clerk
    sprintf(name,"foodClerkLineLock");
    concessClerkLineLock=new Lock(name);
    for(i=0; i<CONCESSIONCLERK_SIZE; i++)
    {
        sprintf(name,"foodClerkMsgLock%d",i);
        concessClerkMsgLock[i]=new Lock(name);
        sprintf(name,"foodclerkline_cv%d",i);
        concessclerkline_cv[i]=new Condition(name);
        sprintf(name,"foodclerkMsg_cv%d",i);
        concessclerkMsg_cv[i]=new Condition(name);
    }
    //step 3: init lock and conditions for the ticket taker
    roomfullLock=new Lock("roomfullLock");
    //  roomfull_cv=new Condition("roomfull_cv");
    seatsReserved=0;
    sprintf(name,"tickettakenLock");
    seatsRsvLock=new Lock(name);
    sprintf(name,"tickettakerLineLock");
    ticketTakerLineLock=new Lock(name);
    for(i=0; i<TICKETTAKER_SIZE; i++)
    {
        sprintf(name,"tickettakerMsgLock%d",i);
        ticketTakerMsgLock[i]=new Lock(name);
        sprintf(name,"tickettakerline_cv%d",i);
        ticketTakerline_cv[i]=new Condition(name);
        sprintf(name,"tickettakerMsg_cv%d",i);
        ticketTakerMsg_cv[i]=new Condition(name);
    }
//step 4:init for the movie technician

    totalCustomerLock=new Lock("totalCustomerLock");
    totalCustomerEnteredLock=new Lock("totalCustomerEnteredLock");

    
    totalCustomerLeftLock=new Lock("totalCustomerLeftLock");

    movieTechMsgLock=new Lock("movieTechMsgLock");
    movieTechMsg_cv=new         Condition("movieTechMsg_cv");

    movie_stateLock=new Lock("movie_stateLock");
    movieState_cv=new Condition("movieState_cv");

}

void createTicketClerk(int num)
{
    DBGPRINT(DEBUG_TRACE,"we are now creating ticketclerk %d \n",num);
    ticketClerk_startWork(num);
}

void createConcessionClerk(int num)
{
    DBGPRINT(DEBUG_TRACE,"we are now creating concessionclerk %d \n",num);
    concessionClerk_startWork(num);
}

void createTicketTaker(int num)
{
    DBGPRINT(DEBUG_TRACE,"we are now creating createTicketTaker %d \n",num);
    ticketTaker_startWork(num);
}

void createMovieTechnician(int num)
{
    DBGPRINT(DEBUG_TRACE,"we are now creating createMovieTechnician %d \n",num);
    movieTechnician_startWork(num);
}
void createCustomers( )
{
    DBGPRINT(DEBUG_TRACE,"we are now creating createCustomers \n");
    generatCustomer();
}

void createManager(int num)
{
    DBGPRINT(DEBUG_TRACE,"we are now creating createManager \n");
    Manager_startWork(0);

}

//This is what is called from main when the command "nachos -P2" is executed
//This runs the movie theater simulation
void Problem2()
{
    char * name = new char [20];					    //Used for creating strings
    char input[100];						    //Used for accepting user input
    int inputValue = 0;
    while(inputValue < 1 || inputValue > 3)
    {
        printf("Press 1 for normal simulation, press 2 for default simulation, 3 for tests:\n>");
        if (fgets(input, sizeof input, stdin))
        {
            sscanf(input, "%d", &inputValue);
        }
    }
    if(inputValue == 1) //Allow user to decide number of entities
    {
        inputValue = 0;
        while(inputValue < 1)
        {
            printf("Enter number of customers:\n>");
            if (fgets(input, sizeof input, stdin))
            {
                sscanf(input, "%d", &inputValue);
            }
        }
        numCustomers = inputValue;
        inputValue = 0;
        while(inputValue < 1 || inputValue > 5)
        {
            printf("Enter number of ticket clerks (1 to 5):\n>");
            if (fgets(input, sizeof input, stdin))
            {
                sscanf(input, "%d", &inputValue);
            }
        }
        numTicketClerks = inputValue;
        inputValue = 0;
        while(inputValue < 1 || inputValue > 5)
        {
            printf("Enter number of concession clerks (1 to 5):\n>");
            if (fgets(input, sizeof input, stdin))
            {
                sscanf(input, "%d", &inputValue);
            }
        }
        numConcessionClerks = inputValue;
        inputValue = 0;
        while(inputValue < 1 || inputValue > 3)
        {
            printf("Enter number of ticket takers (1 to 3):\n>");
            if (fgets(input, sizeof input, stdin))
            {
                sscanf(input, "%d", &inputValue);
            }
        }
        numTicketTakers = inputValue;
    }
    else if (inputValue == 3)
    {
        CustomerData* current;
        inputValue = 0;
        while (inputValue < 1 || inputValue > 6)
        {
            printf("Which test would you like to run?\n");
            printf("Test 1: Customers always take shortest line, but no 2 customers ever choose the same shortest line at the same time.\n");
            printf("Test 2: Managers only read from one clerk's total money received at a time.\n");
            printf("Test 3: Customers do not leave a Clerk, or TicketTaker, until they are told to do so. Clerks and TicketTakers do not start with another Customer until they know the current Customer has left. customer until they know that the last Customer has left their area.\n");
            printf("Test 4: Managers get Clerks off their break when lines get too long.\n");
            printf("Test 5: Total sales never suffers from a race condition.\n");
            printf("Test 6: Customer groups always move together through the theater. This requires explicit synchronization that you implement.\n>");
            if(fgets(input, sizeof input, stdin))
            {
                sscanf(input, "%d", &inputValue);
            }
        }
        Thread *managerThreads;
        switch(inputValue)
        {
        case 2:
            printf("\n\nTest 2: Managers only read from one clerk's total money received at a time.\nSee Test 1:\n");
        case 1:
            printf("Test 1: Customers always take shortest line, but no 2 customers ever choose the same shortest line at the same time.\n\n");
            numCustomers = 2;
            numTicketClerks = 2;
            numGroups=2;
            initTheater();
            printf("Number of Customers = [%d]\n", numCustomers);
            printf("Number of Groups = [%d]\n", numGroups);
            printf("Number of TicketClerks = [%d]\n", numTicketClerks);
            (new Thread("TicketClerk 0"))->Fork(createTicketClerk, 0);
            (new Thread("TicketClerk 1"))->Fork(createTicketClerk, 1);
            current = new CustomerData;
            current->customerNumber = 0;
            current->groupNumber = 0;
            current->groupSize = 1;
            (new Thread("Customer 1"))->Fork(headCustomer_start, (int)current);
            current = new CustomerData;
            current->customerNumber = 1;
            current->groupNumber = 1;
            current->groupSize = 1;
            (new Thread("Customer 2"))->Fork(headCustomer_start, (int)current);
            return;

        case 3:
            printf("\n\nTest 3: Customers do not leave a Clerk, or TicketTaker, until they are told to do so. Clerks and TicketTakers do not start with another Customer until they know the current Customer has left their area.\n\n");
            numCustomers = 2;
            numTicketClerks = 1;
            numTicketTakers = 1;
            numConcessionClerks = 1;
            numGroups=2;
            initTheater();
            printf("Number of Customers = [%d]\n", numCustomers);
            printf("Number of Groups = [%d]\n", numGroups);
            printf("Number of TicketClerks = [%d]\n", numTicketClerks);
            printf("Number of ConcessionClerks = [%d]\n", numConcessionClerks);
            printf("Number of TicketTakers = [%d]\n", numTicketTakers);
            (new Thread("TicketClerk 0"))->Fork(createTicketClerk, 0);
            (new Thread("ConcessionClerk 0"))->Fork(createConcessionClerk, 0);
            (new Thread("TicketTaker 0"))->Fork(createTicketTaker, 0);
            (new Thread("MovieTechnician 0"))->Fork(createMovieTechnician, 0);
            (new Thread("Manager 0"))->Fork(createManager, 0);
            current = new CustomerData;
            current->customerNumber = 0;
            current->groupNumber = 0;
            current->groupSize = 1;
            (new Thread("Customer 0"))->Fork(headCustomer_start, (int)current);
            current = new CustomerData;
            current->customerNumber = 1;
            current->groupNumber = 1;
            current->groupSize = 1;
            (new Thread("Customer 1"))->Fork(headCustomer_start, (int)current);
            return;


        case 4:
            printf("\n\nTest 4: Managers get Clerks off their break when lines get too long.\n");
            numCustomers=10;
            numTicketClerks=1;
            numGroups=10;
            initTheater();
            ticketclrk_off[0]=OFF_WORK;
            printf("Number of Customers = [%d]\n", numCustomers);
            printf("Number of Groups = [%d]\n", numGroups);
            printf("Number of TicketClerks = [%d]\n", numTicketClerks);
            for(unsigned int i = 0; i < numTicketClerks; i++)
            {
                sprintf(name, "TicketClerk %d", i);
                (new Thread(name))->Fork(createTicketClerk, i);
            }
            for(unsigned int i=0; i<numCustomers; i++)
            {
                current = new CustomerData;;
                current->customerNumber = i;
                current->groupNumber = i;
                current->groupSize = 1;
                sprintf(name, "Customer %d", i);
                (new Thread(name))->Fork(headCustomer_start, (int)current);
            }
            managerThreads = new Thread("manager");
            managerThreads->Fork(createManager, 0);

            return;

        case 6:
            printf("\n\nTest 6: Customer groups always move together through the theater. This requires explicit synchronization that you implement.\nSee Test 5:");
        case 5:
            printf("\n\nTest 5: Total sales never suffers from a race condition.\n\n");
            numCustomers=5;
            numTicketClerks=5;
            numGroups=5;
            initTheater();
            printf("Number of Customers = [%d]\n", numCustomers);
            printf("Number of Groups = [%d]\n", numGroups);
            printf("Number of TicketClerks = [%d]\n", numTicketClerks);
            for(unsigned int i = 0; i < numTicketClerks; i++)
            {
                sprintf(name, "TicketClerk %d", i);
                (new Thread(name))->Fork(createTicketClerk, i);
            }
            for(unsigned int i=0; i<numCustomers; i++)
            {
                current = new CustomerData;
                current->customerNumber = i;
                current->groupNumber = i;
                current->groupSize = 1;
                sprintf(name, "Customer %d", i);
                (new Thread(name))->Fork(headCustomer_start, (int)current);
            }
            managerThreads = new Thread("managerThreads");
            managerThreads->Fork(createManager,0);
            return;

        default:
            return;

        }
    }
    else //Input value equals 2, default simulation
    {
        printf("Using hard coded values.\n");
        numCustomers = 40;
        numTicketClerks = 5;
        numConcessionClerks = 5;
        numTicketTakers = 3;
    }

    divideIntoGroups();

    printf("\n");
    printf("Number of Customers = [%d]\n", numCustomers);
    printf("Number of Groups = [%d]\n", numGroups);
    printf("Number of TicketClerks = [%d]\n", numTicketClerks);
    printf("Number of ConcessionClerks = [%d]\n", numConcessionClerks);
    printf("Number of TicketTakers = [%d]\n", numTicketTakers);
    printf("\n");

    initTheater();


    Thread* ticketclrkThreads[numTicketClerks];
    Thread* concessionclrkThreads[numConcessionClerks];
    Thread* tickettakerThreads[numTicketTakers];
    Thread* movietechnicianThreads;
    Thread* managerThreads;
    for(unsigned int i=0; i<numTicketClerks; i++)
    {
        sprintf(name,"ticketclrkThreads%d",i);
        ticketclrkThreads[i] = new Thread(name);
        ticketclrkThreads[i]->Fork(createTicketClerk, i);
    }
    for(unsigned int i=0; i<numConcessionClerks; i++)
    {
        sprintf(name,"concessionclrkThreads%d",i);
        concessionclrkThreads[i] = new Thread(name);
        concessionclrkThreads[i]->Fork(createConcessionClerk, i);
    }
    for(unsigned int i=0; i<numTicketTakers; i++)
    {
        sprintf(name,"TictakerThreads%d",i);
        tickettakerThreads[i] = new Thread(name);
        tickettakerThreads[i]->Fork(createTicketTaker, i);
    }
    movietechnicianThreads = new Thread("movieTechThread");
    movietechnicianThreads->Fork(createMovieTechnician,0);
    managerThreads = new Thread("managerThreads");
    managerThreads->Fork(createManager,0);
    createCustomers();
}

